package com.xyazm.order.statemachine.action.aftersale;

import com.google.common.collect.Lists;
import com.xyazm.common.constants.RedisLockKeyConstants;
import com.xyazm.common.enums.*;
import com.xyazm.order.domain.dto.AfterSaleStateMachineDTO;
import com.xyazm.order.domain.dto.CancelOrderRefundAmountDTO;
import com.xyazm.order.domain.dto.OrderInfoDTO;
import com.xyazm.order.domain.dto.OrderItemDTO;
import com.xyazm.order.domain.entity.*;
import com.xyazm.order.domain.mq.ActualRefundDTO;
import com.xyazm.order.domain.param.CancelOrderAssembleParam;
import com.xyazm.order.enums.*;
import com.xyazm.order.exception.OrderBizException;
import com.xyazm.order.exception.OrderErrorCodeEnum;
import com.xyazm.order.statemachine.action.AfterSaleStateAction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * 取消订单 创建售后信息 Action
 * @author xuyou
 * @date 2023年02月25日 12:01
 */
@Slf4j
@Component
public class CancelOrderCreatedInfoAction extends AfterSaleStateAction<AfterSaleStateMachineDTO> {

    @Resource
    private TransactionTemplate transactionTemplate;

    @Override
    public AfterSaleStateChangeEnum event() {
        return AfterSaleStateChangeEnum.CANCEL_ORDER;
    }

    @Override
    protected AfterSaleStateMachineDTO onStateChangeInternal(AfterSaleStateChangeEnum event,
                                                             AfterSaleStateMachineDTO afterSaleStateMachineDTO) {
        // @Transactional无法生效，需要用编程式事务
        return transactionTemplate.execute(transactionStatus -> {
            CancelOrderAssembleParam cancelOrderAssembleParam = afterSaleStateMachineDTO.getCancelOrderAssembleParam();
            OrderInfoDTO orderInfoDTO = cancelOrderAssembleParam.getOrderInfoDTO();
            String orderId = cancelOrderAssembleParam.getOrderId();
            //  分布式锁
            String key = RedisLockKeyConstants.REFUND_KEY + orderId;
            boolean lock = redisLock.tryLock(key);
            if (!lock) {
                throw new OrderBizException(OrderErrorCodeEnum.PROCESS_REFUND_REPEAT);
            }
            try {
                // 执行退款前的准备工作
                String afterSaleId = orderNoManager.genOrderId(OrderNoTypeEnum.AFTER_SALE.getCode(), orderInfoDTO.getUserId());
                cancelOrderAssembleParam.setAfterSaleId(afterSaleId);

                // 1、计算退款金额
                CancelOrderRefundAmountDTO cancelOrderRefundAmountDTO = calculatingCancelOrderRefundAmount(orderInfoDTO);
                cancelOrderAssembleParam.setCancelOrderRefundAmountDTO(cancelOrderRefundAmountDTO);

                // 2、新增售后信息
                insertAfterSaleInfo(afterSaleStateMachineDTO, event);

                //  3、发送实际退款MQ
                sendActualRefundMessage(cancelOrderAssembleParam);
            } finally {
                redisLock.unlock(key);
            }
            return afterSaleStateMachineDTO;
        });
    }

    /**
     * 发送实际退款MQ
     * @author xuyou
     * 2023-02-25 12:40
     * @param cancelOrderAssembleParam
    */
    private void sendActualRefundMessage(CancelOrderAssembleParam cancelOrderAssembleParam) {
        ActualRefundDTO actualRefundDTO = new ActualRefundDTO();
        actualRefundDTO.setOrderId(cancelOrderAssembleParam.getOrderId());
        actualRefundDTO.setAfterSaleId(cancelOrderAssembleParam.getAfterSaleId());
        // 售后类型 - 退款
        actualRefundDTO.setAfterSaleType(AfterSaleTypeEnum.RETURN_MONEY.getCode());
        mqMessageService.sendActualRefundMessage(actualRefundDTO);
    }

    /**
     * 新增售后信息
     * @author xuyou
     * 2023-02-25 12:12
     * @param afterSaleStateMachineDTO
     * @param afterSaleStateChangeEnum
    */
    private void insertAfterSaleInfo(AfterSaleStateMachineDTO afterSaleStateMachineDTO,
                                     AfterSaleStateChangeEnum afterSaleStateChangeEnum) {
        CancelOrderAssembleParam cancelOrderAssembleParam = afterSaleStateMachineDTO.getCancelOrderAssembleParam();
        String orderId = cancelOrderAssembleParam.getOrderId();
        OrderInfoDO orderInfoDO = orderInfoDAO.getByOrderId(orderId);
        String afterSaleId = cancelOrderAssembleParam.getAfterSaleId();

        //  取消订单过程中的 申请退款金额 和 实际退款金额 都是实付退款金额 金额相同
        AfterSaleInfoDO afterSaleInfoDO = new AfterSaleInfoDO();
        afterSaleInfoDO.setApplyRefundAmount(orderInfoDO.getPayAmount());
        afterSaleInfoDO.setRealRefundAmount(orderInfoDO.getPayAmount());

        // 1、新增售后订单表
        insertCancelOrderAfterSaleInfoTable(orderInfoDO, afterSaleStateChangeEnum.getToStatus().getCode(),
                afterSaleInfoDO, afterSaleId);

        // 2、新增售后条目表
        List<OrderItemDTO> orderItemDTOList = cancelOrderAssembleParam.getOrderItemDTOList();
        insertAfterSaleItemTable(orderId, orderItemDTOList, afterSaleId);

        // 3、新增售后变更表
        OrderInfoDTO orderInfoDTO = cancelOrderAssembleParam.getOrderInfoDTO();
        insertCancelOrderAfterSaleLogTable(afterSaleId, orderInfoDTO,
                afterSaleStateChangeEnum.getFromStatus().getCode(),
                afterSaleStateChangeEnum.getToStatus().getCode());

        // 4、新增售后退款单表
        AfterSaleRefundDO afterSaleRefundDO = insertAfterSaleRefundTable(orderInfoDTO, afterSaleId, afterSaleInfoDO);
        // 设置售后退款单id
        cancelOrderAssembleParam.setAfterSaleRefundId(afterSaleRefundDO.getId());
    }

    /**
     * 新增售后退款单表
     * @author xuyou
     * 2023-02-25 12:31
     * @param orderInfoDTO
     * @param afterSaleId
     * @param afterSaleInfoDO
    */
    private AfterSaleRefundDO insertAfterSaleRefundTable(OrderInfoDTO orderInfoDTO, String afterSaleId,
                                                         AfterSaleInfoDO afterSaleInfoDO) {
        String orderId = orderInfoDTO.getOrderId();
        // 获取售后退款信息
        AfterSaleRefundDO afterSaleRefundDO = super.buildAfterSaleRefundDO(orderId, afterSaleId);
        afterSaleRefundDO.setRefundAmount(afterSaleInfoDO.getRealRefundAmount());
        afterSaleRefundDAO.save(afterSaleRefundDO);
        log.info("新增售后退款单表,订单号:{},售后单号:{},状态:{}", orderId, afterSaleId, afterSaleRefundDO.getRefundStatus());
        return afterSaleRefundDO;
    }

    /**
     * 新增售后变更表
     * @author xuyou
     * 2023-02-25 12:29
     * @param afterSaleId
     * @param orderInfoDTO
     * @param preAfterSaleStatus
     * @param currentAfterSaleStatus
    */
    private void insertCancelOrderAfterSaleLogTable(String afterSaleId, OrderInfoDTO orderInfoDTO,
                                                    Integer preAfterSaleStatus, Integer currentAfterSaleStatus) {
        AfterSaleLogDO afterSaleLogDO = new AfterSaleLogDO();
        afterSaleLogDO.setAfterSaleId(afterSaleId);
        afterSaleLogDO.setOrderId(orderInfoDTO.getOrderId());
        afterSaleLogDO.setPreStatus(preAfterSaleStatus);
        afterSaleLogDO.setCurrentStatus(currentAfterSaleStatus);

        //  取消订单的业务值
        Integer cancelType = Integer.valueOf(orderInfoDTO.getCancelType());
        if (OrderCancelTypeEnum.USER_CANCELED.getCode().equals(cancelType)) {
            afterSaleLogDO.setRemark(OrderCancelTypeEnum.USER_CANCELED.getMsg());
        }
        if (OrderCancelTypeEnum.TIMEOUT_CANCELED.getCode().equals(cancelType)) {
            afterSaleLogDO.setRemark(OrderCancelTypeEnum.TIMEOUT_CANCELED.getMsg());
        }

        afterSaleLogDAO.save(afterSaleLogDO);
        log.info("新增售后单变更信息, 订单号:{},售后单号:{},状态:PreStatus{},CurrentStatus:{}", orderInfoDTO.getOrderId(),
                afterSaleId, afterSaleLogDO.getPreStatus(), afterSaleLogDO.getCurrentStatus());
    }
    /**
     * 新增售后条目表
     * @author xuyou
     * 2023-02-25 12:24
     * @param orderId
     * @param orderItemDTOList
     * @param afterSaleId
    */
    private void insertAfterSaleItemTable(String orderId, List<OrderItemDTO> orderItemDTOList, String afterSaleId) {
        List<AfterSaleItemDO> itemDOList = Lists.newArrayList();
        for (OrderItemDTO orderItem : orderItemDTOList) {
            AfterSaleItemDO afterSaleItemDO = new AfterSaleItemDO();
            afterSaleItemDO.setAfterSaleId(afterSaleId);
            afterSaleItemDO.setOrderId(orderId);
            afterSaleItemDO.setSkuCode(orderItem.getSkuCode());
            afterSaleItemDO.setProductName(orderItem.getProductName());
            afterSaleItemDO.setProductImg(orderItem.getProductImg());
            afterSaleItemDO.setReturnQuantity(orderItem.getSaleQuantity());
            afterSaleItemDO.setOriginAmount(orderItem.getOriginAmount());
            afterSaleItemDO.setApplyRefundAmount(orderItem.getOriginAmount());
            afterSaleItemDO.setRealRefundAmount(orderItem.getPayAmount());
            // 取消订单 条目中的sku全部退货
            // 本条目退货完成标记 20:购买的sku已全部退货
            afterSaleItemDO.setReturnCompletionMark(AfterSaleReturnCompletionMarkEnum.ALL_RETURN_GOODS.getCode());
            // 售后条目类型 10:售后订单条目
            afterSaleItemDO.setAfterSaleItemType(AfterSaleItemTypeEnum.AFTER_SALE_ORDER_ITEM.getCode());
            itemDOList.add(afterSaleItemDO);
        }
        afterSaleItemDAO.saveBatch(itemDOList);
    }


    /**
     * 新增售后订单表
     * @author xuyou
     * @date: 2022/6/29 14:26
     */
    private void insertCancelOrderAfterSaleInfoTable(OrderInfoDO orderInfoDO, Integer cancelOrderAfterSaleStatus,
                                                                AfterSaleInfoDO afterSaleInfoDO, String afterSaleId) {
        afterSaleInfoDO.setAfterSaleId(afterSaleId);
        afterSaleInfoDO.setBusinessIdentifier(BusinessIdentifierEnum.SELF_MALL.getCode());
        afterSaleInfoDO.setOrderId(orderInfoDO.getOrderId());
        afterSaleInfoDO.setOrderSourceChannel(BusinessIdentifierEnum.SELF_MALL.getCode());
        afterSaleInfoDO.setUserId(orderInfoDO.getUserId());
        afterSaleInfoDO.setOrderType(OrderTypeEnum.NORMAL.getCode());
        afterSaleInfoDO.setApplyTime(new Date());
        afterSaleInfoDO.setAfterSaleStatus(cancelOrderAfterSaleStatus);

        afterSaleInfoDO.setRealRefundAmount(afterSaleInfoDO.getRealRefundAmount());
        afterSaleInfoDO.setApplyRefundAmount(afterSaleInfoDO.getApplyRefundAmount());
        //  取消订单 整笔退款
        afterSaleInfoDO.setAfterSaleType(AfterSaleTypeEnum.RETURN_MONEY.getCode());

        Integer cancelType = orderInfoDO.getCancelType();
        if (OrderCancelTypeEnum.TIMEOUT_CANCELED.getCode().equals(cancelType)) {
            afterSaleInfoDO.setAfterSaleTypeDetail(AfterSaleTypeDetailEnum.TIMEOUT_NO_PAY.getCode());
            afterSaleInfoDO.setRemark("超时未支付自动取消");
        }
        if (OrderCancelTypeEnum.USER_CANCELED.getCode().equals(cancelType)) {
            afterSaleInfoDO.setAfterSaleTypeDetail(AfterSaleTypeDetailEnum.USER_CANCEL.getCode());
            afterSaleInfoDO.setRemark("用户手动取消");
        }
        afterSaleInfoDO.setApplyReasonCode(AfterSaleReasonEnum.CANCEL.getCode());
        afterSaleInfoDO.setApplyReason(AfterSaleReasonEnum.CANCEL.getMsg());
        afterSaleInfoDO.setApplySource(AfterSaleApplySourceEnum.SYSTEM.getCode());
        afterSaleInfoDO.setReviewTime(new Date());

        afterSaleInfoDAO.save(afterSaleInfoDO);

        log.info("新增订单售后记录,订单号:{},售后单号:{},订单售后状态:{}", afterSaleInfoDO.getOrderId(),
                afterSaleInfoDO.getAfterSaleId(), afterSaleInfoDO.getAfterSaleStatus());
    }


    /**
     * 计算需要退款的金额
     * @author xuyou
     * @date: 2022/6/29 10:34
     * @param: param
     */
    private CancelOrderRefundAmountDTO calculatingCancelOrderRefundAmount(OrderInfoDTO orderInfoDTO) {
        // 退整笔订单的实际支付金额
        return CancelOrderRefundAmountDTO.builder()
                .orderId(orderInfoDTO.getOrderId())
                .totalAmount(orderInfoDTO.getTotalAmount())
                .returnGoodAmount(orderInfoDTO.getPayAmount())
                .build();
    }

}
